﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UITesting.WpfControls;

namespace CodedUITestExtensions
{
    /// <summary>
    /// Extensions for grouping WPFControl (WpfTable, WpfList etc.).
    /// </summary>
    public static class GroupingWpfControlExtensions
    {
        /// <summary>
        ///  Returns a collection of child controls of type T for grouping control. 
        /// </summary>
        /// <typeparam name="T">Child control type.</typeparam>
        /// <param name="parentControl">Grouping control.</param>
        public static IList<T> GetChildren<T>(this WpfControl parentControl) where T : WpfControl
        {
            if (parentControl == null)
            {
                throw new ArgumentNullException("parentControl");
            }

            Type type = typeof(T);
            var control = (T)Activator.CreateInstance(type, parentControl);
            IList<T> childControls = control.FindMatchingControls().Select(c =>
            {
                var item = (T)Activator.CreateInstance(type, parentControl);
                item.CopyFrom(c);
                return item;
            }
                ).ToList();
            return childControls;
        }

        /// <summary>
        /// Finds a child control by a specified condition among children of  grouping control.
        /// </summary>
        /// <typeparam name="T">Child control type.</typeparam>
        /// <param name="parentControl">Grouping control.</param>
        /// <param name="predicate">Search condition.</param>
        /// <returns>Child control.</returns>
        public static T FindChildControl<T>(this WpfControl parentControl, Func<T, bool> predicate) 
            where T : WpfControl
        {
            if (parentControl == null)
            {
                throw new ArgumentNullException("parentControl");
            }

            T resultControl = parentControl.GetChildren<T>().FirstOrDefault(predicate);

            return resultControl;
        }

        /// <summary>
        /// Finds a child control by a specified condition among children of  grouping control. Search starts from last child.
        /// </summary>
        /// <typeparam name="T">Child control type.</typeparam>
        /// <param name="parentControl">Grouping control.</param>
        /// <param name="predicate">Search condition.</param>
        /// <returns>Child control.</returns>
        public static T FindChildControlFromLast<T>(this WpfControl parentControl, Func<T, bool> predicate)
            where T : WpfControl
        {
            if (parentControl == null)
            {
                throw new ArgumentNullException("parentControl");
            }

            T resultControl = parentControl.GetChildren<T>().LastOrDefault(predicate);

            return resultControl;
        }


        /// <summary>
        /// Finds a child control by a specified condition among children of  grouping control
        ///  in first N children.
        /// </summary>
        /// <typeparam name="T">Child control type.</typeparam>
        /// <param name="parentControl">Grouping control.</param>
        /// <param name="rowCount">Count of children including searched control.</param>
        /// <param name="predicate">Search condition.</param>
        /// <returns>Child control.</returns>
        public static T FindChildControl<T>(this WpfControl parentControl, int rowCount, Func<T, bool> predicate) 
            where T : WpfControl
        {
            if (parentControl == null)
            {
                throw new ArgumentNullException("parentControl");
            }

            IEnumerable<T> controls = parentControl.GetFirstNChildren<T>(rowCount);
            T resultControl = controls.FirstOrDefault(predicate);
            return resultControl;
        }

        /// <summary>
        /// Finds all children by a specified condition among children of  grouping control.
        /// </summary>
        /// <typeparam name="T">Child control type.</typeparam>
        /// <param name="parentControl">Grouping control.</param>
        /// <param name="predicate">Search condition.</param>
        /// <returns>Children.</returns>
        public static IEnumerable<T> FindChildren<T>(this WpfControl parentControl, Func<T, bool> predicate)
            where T: WpfControl
        {
            if (parentControl == null)
            {
                throw new ArgumentNullException("parentControl");
            }

            IEnumerable<T> resultRow = parentControl.GetChildren<T>().Where(predicate).ToList();

            return resultRow;
        }

        /// <summary>
        /// Finds child control index.
        /// </summary>
        /// <typeparam name="T">Child control type.</typeparam>
        /// <param name="parentControl">Grouping control.</param>
        /// <param name="childControl">Child control.</param>
        /// <returns>Child control index.</returns>
        public static int GetChildIndex<T>(this WpfControl parentControl, T childControl)
            where T : WpfControl
        {
            if (parentControl == null)
            {
                throw new ArgumentNullException("parentControl");
            }
            if (parentControl == null)
            {
                throw new ArgumentNullException("childControl");
            }

            int index = parentControl.GetChildren<T>().ToList().IndexOf(childControl);
            return index;
        }

        /// <summary>
        /// Gets first N children of grouping control.
        /// </summary>
        /// <typeparam name="T">Child control type.</typeparam>
        /// <param name="parentControl">Grouping control.</param>
        /// <param name="count">Children count.</param>
        /// <returns>Children.</returns>
        private static IList<T> GetFirstNChildren<T>(this WpfControl parentControl, int count) where T: WpfControl
        {
            if (parentControl == null)
            {
                throw new ArgumentNullException("parentControl");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            IList<T> resultControls = new List<T>() { };
            Type type = typeof(T);
            for (int i = 0; i < count; ++i)
            {
                string instance = (i + 1).ToString();
                var control = (T)Activator.CreateInstance(type, parentControl);
                control.SearchProperties["Instance"] = instance;
                bool result = control.TryFind();
                if (result)
                {
                    resultControls.Add(control);
                }
                else
                {
                    break;
                }
            }

            return resultControls;
        }
    }
}
